library(lattice)
xyplot(speed ~ dist, cars)

#ggplot(cars, x=speed, y=dist)
p1 <- ggplot() + geom_line(aes(y = dist, x = speed, color=speed),
                           data = cars, stat="identity")
p1

#Install packages, load libraries, check version of ggplot2
#install.packages("ggplot2")
library("ggplot2"); packageVersion("ggplot2")
[1] ‘3.0.0’
#Load data
data("midwest", package = "ggplot2")  # load the data

#If the first doesn't work try this
# midwest <- read.csv("http://goo.gl/G1K41K") # alt source 
# Turn off scientific notation like 1e+06
options(scipen=999)  
str(midwest)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   437 obs. of  28 variables:
 $ PID                 : int  561 562 563 564 565 566 567 568 569 570 ...
 $ county              : chr  "ADAMS" "ALEXANDER" "BOND" "BOONE" ...
 $ state               : chr  "IL" "IL" "IL" "IL" ...
 $ area                : num  0.052 0.014 0.022 0.017 0.018 0.05 0.017 0.027 0.024 0.058 ...
 $ poptotal            : int  66090 10626 14991 30806 5836 35688 5322 16805 13437 173025 ...
 $ popdensity          : num  1271 759 681 1812 324 ...
 $ popwhite            : int  63917 7054 14477 29344 5264 35157 5298 16519 13384 146506 ...
 $ popblack            : int  1702 3496 429 127 547 50 1 111 16 16559 ...
 $ popamerindian       : int  98 19 35 46 14 65 8 30 8 331 ...
 $ popasian            : int  249 48 16 150 5 195 15 61 23 8033 ...
 $ popother            : int  124 9 34 1139 6 221 0 84 6 1596 ...
 $ percwhite           : num  96.7 66.4 96.6 95.3 90.2 ...
 $ percblack           : num  2.575 32.9 2.862 0.412 9.373 ...
 $ percamerindan       : num  0.148 0.179 0.233 0.149 0.24 ...
 $ percasian           : num  0.3768 0.4517 0.1067 0.4869 0.0857 ...
 $ percother           : num  0.1876 0.0847 0.2268 3.6973 0.1028 ...
 $ popadults           : int  43298 6724 9669 19272 3979 23444 3583 11323 8825 95971 ...
 $ perchsd             : num  75.1 59.7 69.3 75.5 68.9 ...
 $ percollege          : num  19.6 11.2 17 17.3 14.5 ...
 $ percprof            : num  4.36 2.87 4.49 4.2 3.37 ...
 $ poppovertyknown     : int  63628 10529 14235 30337 4815 35107 5241 16455 13081 154934 ...
 $ percpovertyknown    : num  96.3 99.1 95 98.5 82.5 ...
 $ percbelowpoverty    : num  13.15 32.24 12.07 7.21 13.52 ...
 $ percchildbelowpovert: num  18 45.8 14 11.2 13 ...
 $ percadultpoverty    : num  11.01 27.39 10.85 5.54 11.14 ...
 $ percelderlypoverty  : num  12.44 25.23 12.7 6.22 19.2 ...
 $ inmetro             : int  0 0 0 1 0 0 0 0 0 1 ...
 $ category            : chr  "AAR" "LHR" "AAR" "ALU" ...
dim(midwest)
[1] 437  28
#data properties -> unix-like head
head(midwest)
#data properties -> unix-like tail
tail(midwest)
#Make a simple scatter plot of the data
ggplot(midwest, aes(x=area, y=poptotal)) + geom_point()

#Remove gray background and assign to variables
p <- ggplot(midwest, aes(x=area, y=poptotal)) + geom_point()
p <- p + theme_bw()
print(p)

#Run a linear model on the data
# set se=FALSE to turnoff confidence bands
g <- ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm") 
g <- g + theme_bw()
print(g)

# Delete the points outside the limits
g <- g + xlim(c(0, 0.1)) + ylim(c(0, 1000000))   # deletes points
print(g)

# Zoom in without deleting the points outside the limits. 
# As a result, the line of best fit is the same as the original plot.
g <- ggplot(midwest, aes(x=area, y=poptotal)) + geom_point() + geom_smooth(method="lm")  
# Zoom in without deleting the points outside the limits. 
# As a result, the line of best fit is the same as the original plot.
g1 <- g + coord_cartesian(xlim=c(0,0.1), ylim=c(0, 1000000)) + theme_bw() # zooms in
print(g1)

# Add Title and Labels
g1 + labs(title="Area Vs Population", subtitle="From midwest dataset", y="Population", x="Area", caption="Midwest Demographics")

print(g1)

# Add Title and Labels
g1 + ggtitle("Area Vs Population", subtitle="From midwest dataset") + xlab("Area") + ylab("Population")

# Full Plot call
library(ggplot2)
q <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point() + theme_bw() +
  geom_smooth(method="lm") + 
  coord_cartesian(xlim=c(0,0.1), ylim=c(0, 1000000)) + 
  labs(title="Area Vs Population", subtitle="From midwest dataset", y="Population", x="Area", caption="Midwest Demographics")
print(q)

#Change the colors
ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(col="steelblue", size=3) +   # Set static color and size for points
  geom_smooth(method="lm", col="firebrick") +  # change the color of line
  coord_cartesian(xlim=c(0, 0.1), ylim=c(0, 1000000)) + theme_bw() +
  labs(title="Area Vs Population", subtitle="From midwest dataset", y="Population", x="Area", caption="Midwest Demographics")

#Color by state
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state), size=3) +  # Set color to vary based on state categories.
  geom_smooth(method="lm", col="firebrick", size=2) + theme_bw() +
  coord_cartesian(xlim=c(0, 0.1), ylim=c(0, 1000000)) + 
  labs(title="Area Vs Population", subtitle="From midwest dataset", y="Population", x="Area", caption="Midwest Demographics")
plot(gg)

# remove legend
gg + theme(legend.position="None")  

gg + scale_colour_brewer(palette = "Set1")  # change color palette

library(RColorBrewer)
head(brewer.pal.info, 10) 
# Base plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state), size=3) +  # Set color to vary based on state categories.
  geom_smooth(method="lm", col="firebrick", size=2) + 
  coord_cartesian(xlim=c(0, 0.1), ylim=c(0, 1000000)) + theme_bw() +
  labs(title="Area Vs Population", subtitle="From midwest dataset", y="Population", x="Area", caption="Midwest Demographics")
# Change breaks
gg + scale_x_continuous(breaks=seq(0, 0.1, 0.01))

# Change breaks + label as Alphabet
gg + scale_x_continuous(breaks=seq(0, 0.1, 0.01), labels = letters[1:11])

# Reverse X Axis Scale
gg + scale_x_reverse()

# Change Axis Texts
gg + scale_x_continuous(breaks=seq(0, 0.1, 0.01), labels = sprintf("%1.2f%%", seq(0, 0.1, 0.01))) + 
  scale_y_continuous(breaks=seq(0, 1000000, 200000), labels = function(x){paste0(x/1000, 'K')})

# method 1: Using theme_set()
theme_set(theme_classic())  # not run
gg

# method 2: Adding theme Layer itself. without grids
gg + theme_classic() + labs(subtitle="Classic Theme")

# Add plot components 
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
# Call plot 
plot(gg)

# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
# Modify theme components 
gg + theme(plot.title=element_text(size=20, 
                                    face="bold", 
                                    family="American Typewriter",
                                    color="tomato",
                                    hjust=0.5,
                                    lineheight=1.2),  # title
            plot.subtitle=element_text(size=15, 
                                       family="American Typewriter",
                                       face="bold",
                                       hjust=0.5),  # subtitle
            plot.caption=element_text(size=15),  # caption
            axis.title.x=element_text(vjust=10,  
                                      size=15),  # X axis title
            axis.title.y=element_text(size=15),  # Y axis title
            axis.text.x=element_text(size=10, 
                                     angle = 30,
                                     vjust=.5),  # X axis text
            axis.text.y=element_text(size=10))  # Y axis text

# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
gg + labs(color="State", size="Density")  # modify legend title

##Method 2: Using guides()
# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
gg <- gg + guides(color=guide_legend("State"), size=guide_legend("Density"))  # modify legend title
plot(gg)

##M Method 3: Using scale_aesthetic_vartype()
# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
# Modify Legend
gg + scale_color_discrete(name="State") + scale_size_continuous(name = "Density", guide = FALSE)  # turn off legend for size

##Manual color scale
# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
gg + scale_color_manual(name="State", 
                        labels = c("Illinois", 
                                   "Indiana", 
                                   "Michigan", 
                                   "Ohio", 
                                   "Wisconsin"), 
                        values = c("IL"="blue", 
                                   "IN"="red", 
                                   "MI"="green", 
                                   "OH"="brown", 
                                   "WI"="orange"))

#Change the Order of Legend
# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
gg + guides(colour = guide_legend(order = 1),
            size = guide_legend(order = 2))

#How to Style the Legend Title, Text and Key
gg + theme(legend.title = element_text(size=12, color = "firebrick"), 
           legend.text = element_text(size=10),
           legend.key=element_rect(fill='springgreen')) + 
  guides(colour = guide_legend(override.aes = list(size=2, stroke=1.5))) 

# No legend 
gg + theme(legend.position="None") + labs(subtitle="No Legend")

# legend at the bottom and horizontal 
gg + theme(legend.position="bottom", legend.box = "horizontal") + labs(subtitle="Legend at Bottom")

# legend at bottom-right, inside the plot 
gg + theme(legend.title = element_text(size=12, color = "salmon", face="bold"),
           legend.justification=c(1,0), 
           legend.position=c(0.95, 0.05),  
           legend.background = element_blank(),
           legend.key = element_blank()) + 
  labs(subtitle="Legend: Bottom-Right Inside the Plot")

# legend at top-left, inside the plot 
gg + theme(legend.title = element_text(size=12, color = "salmon", face="bold"),
           legend.justification=c(0,1), 
           legend.position=c(0.05, 0.95),
           legend.background = element_blank(),
           legend.key = element_blank()) + 
  labs(subtitle="Legend: Top-Left Inside the Plot")

# Filter required rows.
midwest_sub <- midwest[midwest$poptotal > 300000, ]
midwest_sub$large_county <- ifelse(midwest_sub$poptotal > 300000, midwest_sub$county, "")
# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest")
# Plot text and label ------------------------------------------------------
gg + geom_text(aes(label=large_county), size=2, data=midwest_sub) + labs(subtitle="With ggplot2::geom_text") + theme(legend.position = "None")   # text

gg + geom_label(aes(label=large_county), size=2, data=midwest_sub, alpha=0.25) + labs(subtitle="With ggplot2::geom_label") + theme(legend.position = "None")  # label

# Plot text and label that REPELS eachother (using ggrepel pkg)
#install.packages("ggrepel")
library(ggrepel)
gg + geom_text_repel(aes(label=large_county), size=2, data=midwest_sub) + labs(subtitle="With ggrepel::geom_text_repel") + theme(legend.position = "None")   # text

gg + geom_label_repel(aes(label=large_county), size=2, data=midwest_sub) + labs(subtitle="With ggrepel::geom_label_repel") + theme(legend.position = "None")   # label

##How to Add Annotations Anywhere inside Plot
# Define and add annotation
library(grid)
my_text <- "This text is at x=0.7 and y=0.8!"
my_grob = grid.text(my_text, x=0.7,  y=0.8, gp=gpar(col="firebrick", fontsize=14, fontface="bold"))
gg + annotation_custom(my_grob)

# Flip the X and Y axis
# Base Plot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest", subtitle="X and Y axis Flipped") + theme(legend.position = "None")
# Flip the X and Y axis 
gg + coord_flip()

#Reverse the scale of an axis
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + xlim(c(0, 0.1)) + ylim(c(0, 500000)) + 
  labs(title="Area Vs Population", y="Population", x="Area", caption="Source: midwest", subtitle="Axis Scales Reversed") + theme(legend.position = "None")
# Reverse the X and Y Axis ---------------------------
gg1 <- gg + scale_x_reverse() + scale_y_reverse()
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
gg1

#Plots side by side
library("grid"); packageVersion("grid")
[1] ‘3.4.4’
library("gridExtra"); packageVersion("gridExtra")
[1] ‘2.3’
#arrange plot
grid.arrange(gg,gg1, ncol=1)

#arrange plot
grid.arrange(gg,gg1, ncol=2)

#arrange plot -> var 3
grid.arrange(gg,gg1,gg, ncol=3)

#rearrange ggplot2 with one large plot on top and two on bottom 
grid.arrange(gg, arrangeGrob(gg1, gg, ncol=2), heights=c(2/4, 1/4), ncol=1) 

#rearrange ggplot2 with one large plot on bottom and two on top
grid.arrange(arrangeGrob(gg1, gg, ncol=2),gg, heights=c(1/4, 2/4), ncol=1)

#Faceting: Draw multiple plots within one figure
data(mpg, package="ggplot2")  # load data
# mpg <- read.csv("http://goo.gl/uEeRGu")  # alt data source
g <- ggplot(mpg, aes(x=displ, y=hwy)) + 
      geom_point() + 
      labs(title="hwy vs displ", caption = "Source: mpg") +
      geom_smooth(method="lm", se=FALSE) + 
      theme_bw()  # apply bw theme
plot(g)

#Facet Wrap
# Base Plot
g <- ggplot(mpg, aes(x=displ, y=hwy)) + 
      geom_point() + 
      geom_smooth(method="lm", se=FALSE) + 
      theme_bw()  # apply bw theme
# Facet wrap with common scales
g + facet_wrap( ~ class, nrow=3) + labs(title="hwy vs displ", caption = "Source: mpg", subtitle="Ggplot2 - Faceting - Multiple plots in one figure")  # Shared scales

# Facet wrap with free scales
g + facet_wrap( ~ class, scales = "free") + labs(title="hwy vs displ", caption = "Source: mpg", subtitle="Ggplot2 - Faceting - Multiple plots in one figure with free scales")  # Scales free

## Facet grid
# Base Plot
g <- ggplot(mpg, aes(x=displ, y=hwy)) + 
      geom_point() + 
      labs(title="hwy vs displ", caption = "Source: mpg", subtitle="Ggplot2 - Faceting - Multiple plots in one figure") +
      geom_smooth(method="lm", se=FALSE) + 
      theme_bw()  # apply bw theme
# Add Facet Grid
g1 <- g + facet_grid(manufacturer ~ class)  # manufacturer in rows and class in columns
plot(g1)

# Base Plot
g <- ggplot(mpg, aes(x=displ, y=hwy)) + 
      geom_point() + 
      geom_smooth(method="lm", se=FALSE) + 
      labs(title="hwy vs displ", caption = "Source: mpg", subtitle="Ggplot2 - Facet Grid - Multiple plots in one figure") +
      theme_bw()  # apply bw theme
# Add Facet Grid
g2 <- g + facet_grid(cyl ~ class)  # cyl in rows and class in columns.
plot(g2)

##Modifying Plot Background, Major and Minor Axis
# Base Plot
g <- ggplot(mpg, aes(x=displ, y=hwy)) + 
      geom_point() + 
      geom_smooth(method="lm", se=FALSE) + 
      theme_bw()  # apply bw theme
# Change Plot Background elements 
g + theme(panel.background = element_rect(fill = 'khaki'),
          panel.grid.major = element_line(colour = "burlywood", size=1.5),
          panel.grid.minor = element_line(colour = "tomato", 
                                          size=.25, 
                                          linetype = "dashed"),
          panel.border = element_blank(),
          axis.line.x = element_line(colour = "darkorange", 
                                     size=1.5, 
                                     lineend = "butt"),
          axis.line.y = element_line(colour = "darkorange", 
                                     size=1.5)) +
    labs(title="Modified Background", 
         subtitle="How to Change Major and Minor grid, Axis Lines, No Border")

# Change Plot Margins -----------------------------------------------
g + theme(plot.background=element_rect(fill="salmon"), 
          plot.margin = unit(c(2, 2, 1, 1), "cm")) +  # top, right, bottom, left
    labs(title="Modified Background", subtitle="How to Change Plot Margin") 

##How to Remove Major and Minor Grid, Change Border, Axis Title, Text and Ticks
# Base Plot
g <- ggplot(mpg, aes(x=displ, y=hwy)) + 
      geom_point() + 
      geom_smooth(method="lm", se=FALSE) + 
      theme_bw()  # apply bw theme
g + theme(panel.grid.major = element_blank(), 
          panel.grid.minor = element_blank(), 
          panel.border = element_blank(),
          axis.title = element_blank(), 
          axis.text = element_blank(),
          axis.ticks = element_blank()) +
  labs(title="Modified Background", subtitle="How to remove major and minor axis grid, border, axis title, text and ticks") 

# Scatterplot
gg <- ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + 
  xlim(c(0, 0.1)) + 
  ylim(c(0, 500000)) + theme_bw() +
  labs(subtitle="Area Vs Population", 
       y="Population", 
       x="Area", 
       title="Scatterplot", 
       caption = "Source: midwest")
plot(gg)

#Scatterplot
g <- ggplot(mpg, aes(cty, hwy))
# Scatterplot
g + geom_point() + 
  geom_smooth(method="lm", se=F) + theme_bw()

  labs(subtitle="mpg: city vs highway mileage", 
       y="hwy", 
       x="cty", 
       title="Scatterplot with overlapping points", 
       caption="Source: midwest")
$subtitle
[1] "mpg: city vs highway mileage"

$y
[1] "hwy"

$x
[1] "cty"

$title
[1] "Scatterplot with overlapping points"

$caption
[1] "Source: midwest"

attr(,"class")
[1] "labels"
# Scatterplot
theme_set(theme_bw())  # pre-set the bw theme.
g <- ggplot(mpg, aes(cty, hwy))
g + geom_jitter(width = .5, size=1) +
  labs(subtitle="mpg: city vs highway mileage", 
       y="hwy", 
       x="cty", 
       title="Jittered Points")

# Scatterplot
theme_set(theme_bw())  # pre-set the bw theme.
g <- ggplot(mpg, aes(cty, hwy))
g + geom_count(col="tomato3", show.legend=F) +
  labs(subtitle="mpg: city vs highway mileage", 
       y="hwy", 
       x="cty", 
       title="Counts Plot")

mpg_select <- mpg[mpg$manufacturer %in% c("audi", "ford", "honda", "hyundai"), ]
# Scatterplot
theme_set(theme_bw())  # pre-set the bw theme.
g <- ggplot(mpg_select, aes(displ, cty)) + 
  labs(subtitle="mpg: Displacement vs City Mileage",
       title="Bubble chart")
g + geom_jitter(aes(col=manufacturer, size=hwy)) + 
  geom_smooth(aes(col=manufacturer), method="lm", se=F)

# Data Prep
data("mtcars")  # load data
mtcars$`car name` <- rownames(mtcars)  # create new column for car names
mtcars$mpg_z <- round((mtcars$mpg - mean(mtcars$mpg))/sd(mtcars$mpg), 2)  # compute normalized mpg
mtcars$mpg_type <- ifelse(mtcars$mpg_z < 0, "below", "above")  # above / below avg flag
mtcars <- mtcars[order(mtcars$mpg_z), ]  # sort
mtcars$`car name` <- factor(mtcars$`car name`, levels = mtcars$`car name`)  # convert to factor to retain sorted order in plot.
# Diverging Barcharts
ggplot(mtcars, aes(x=`car name`, y=mpg_z, label=mpg_z)) + 
  geom_bar(stat='identity', aes(fill=mpg_type), width=.5)  +
  scale_fill_manual(name="Mileage", 
                    labels = c("Above Average", "Below Average"), 
                    values = c("above"="#00ba38", "below"="#f8766d")) + 
  labs(subtitle="Normalised mileage from 'mtcars'", 
       title= "Diverging Bars") + 
  coord_flip()

ggplot(mtcars, aes(x=`car name`, y=mpg_z, label=mpg_z)) + 
  geom_point(stat='identity', fill="black", size=6)  +
  geom_segment(aes(y = 0, 
                   x = `car name`, 
                   yend = mpg_z, 
                   xend = `car name`), 
               color = "black") +
  geom_text(color="white", size=2) +
  labs(title="Diverging Lollipop Chart", 
       subtitle="Normalized mileage from 'mtcars': Lollipop") + 
  ylim(-2.5, 2.5) +
  coord_flip()

# Plot
ggplot(mtcars, aes(x=`car name`, y=mpg_z, label=mpg_z)) + 
  geom_point(stat='identity', aes(col=mpg_type), size=6)  +
  scale_color_manual(name="Mileage", 
                     labels = c("Above Average", "Below Average"), 
                     values = c("above"="#00ba38", "below"="#f8766d")) + 
  geom_text(color="white", size=2) +
  labs(title="Diverging Dot Plot", 
       subtitle="Normalized mileage from 'mtcars': Dotplot") + 
  ylim(-2.5, 2.5) +
  coord_flip()

# Prepare data: group mean city mileage by manufacturer.
cty_mpg <- aggregate(mpg$cty, by=list(mpg$manufacturer), FUN=mean)  # aggregate
colnames(cty_mpg) <- c("make", "mileage")  # change column names
cty_mpg <- cty_mpg[order(cty_mpg$mileage), ]  # sort
cty_mpg$make <- factor(cty_mpg$make, levels = cty_mpg$make)  # to retain the order in plot.
head(cty_mpg, 4)
#>          make  mileage
#> 9     lincoln 11.33333
#> 8  land rover 11.50000
#> 3       dodge 13.13514
#> 10    mercury 13.25000
# Draw plot
ggplot(cty_mpg, aes(x=make, y=mileage)) + 
  geom_bar(stat="identity", width=.5, fill="tomato3") + 
  labs(title="Ordered Bar Chart", 
       subtitle="Make Vs Avg. Mileage", 
       caption="source: mpg") + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6))

# Plot
ggplot(cty_mpg, aes(x=make, y=mileage)) + 
  geom_point(size=3) + 
  geom_segment(aes(x=make, 
                   xend=make, 
                   y=0, 
                   yend=mileage)) + 
  labs(title="Lollipop Chart", 
       subtitle="Make Vs Avg. Mileage", 
       caption="source: mpg") + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6))

# Histogram on a Continuous (Numeric) Variable
g <- ggplot(mpg, aes(displ)) + scale_fill_brewer(palette = "Spectral")
g + geom_histogram(aes(fill=class), 
                   binwidth = .1, 
                   col="black", 
                   size=.1) +  # change binwidth
  labs(title="Histogram with Auto Binning", 
       subtitle="Engine Displacement across Vehicle Classes")

# Histogram on a Continuous (Numeric) Variable
g + geom_histogram(aes(fill=class), 
                   bins=5, 
                   col="black", 
                   size=.1) +   # change number of bins
  labs(title="Histogram with Fixed Bins", 
       subtitle="Engine Displacement across Vehicle Classes") 

# Histogram on a Categorical variable
g <- ggplot(mpg, aes(manufacturer))
g + geom_bar(aes(fill=class), width = 0.5) + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6)) + 
  labs(title="Histogram on Categorical Variable", 
       subtitle="Manufacturer across Vehicle Classes") 

# Plot
g <- ggplot(mpg, aes(cty))
g + geom_density(aes(fill=factor(cyl)), alpha=0.8) + 
    labs(title="Density plot", 
         subtitle="City Mileage Grouped by Number of cylinders",
         caption="Source: mpg",
         x="City Mileage",
         fill="# Cylinders")

# Box Plot
g <- ggplot(mpg, aes(class, cty))
g + geom_boxplot(varwidth=T, fill="plum") + 
    labs(title="Box plot", 
         subtitle="City Mileage grouped by Class of vehicle",
         caption="Source: mpg",
         x="Class of Vehicle",
         y="City Mileage")

library(ggthemes)
g <- ggplot(mpg, aes(class, cty))
g + geom_boxplot(aes(fill=factor(cyl))) + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6)) + 
  labs(title="Box plot", 
       subtitle="City Mileage grouped by Class of vehicle",
       caption="Source: mpg",
       x="Class of Vehicle",
       y="City Mileage")

# dot + boxplot
g <- ggplot(mpg, aes(manufacturer, cty))
g + geom_boxplot() + 
  geom_dotplot(binaxis='y', 
               stackdir='center', 
               dotsize = .5, 
               fill="red") +
  theme(axis.text.x = element_text(angle=65, vjust=0.6)) + 
  labs(title="Box plot + Dot plot", 
       subtitle="City Mileage vs Class: Each dot represents 1 row in source data",
       caption="Source: mpg",
       x="Class of Vehicle",
       y="City Mileage")

# violin plot
g <- ggplot(mpg, aes(class, cty))
g + geom_violin() + 
  labs(title="Violin plot", 
       subtitle="City Mileage vs Class of vehicle",
       caption="Source: mpg",
       x="Class of Vehicle",
       y="City Mileage")

# prep frequency table
freqtable <- table(mpg$manufacturer)
df <- as.data.frame.table(freqtable)
head(df)
NA
# BarPlot
g <- ggplot(df, aes(Var1, Freq))
g + geom_bar(stat="identity", width = 0.5, fill="tomato2") + 
      labs(title="Bar Chart", 
           subtitle="Manufacturer of vehicles", 
           caption="Source: Frequency of Manufacturers from 'mpg' dataset") +
      theme(axis.text.x = element_text(angle=65, vjust=0.6))

LS0tCnRpdGxlOiAiZ2dwbG90MiB0dXRvcmlhbCBSIG5vdGVib29rIgphdXRob3I6ICJEci4gUmljaGFyZCBBbGxlbiBXaGl0ZSBJSUkiCmRhdGUgY3JlYXRlZDogIk9jdCAyMiwgMjAxOCIKbGFzdCB1cGRhdGU6ICJPY3QgMjIsIDIwMTgiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQpwbG90KGNhcnMpCmBgYAoKYGBge3J9CmxpYnJhcnkobGF0dGljZSkKeHlwbG90KHNwZWVkIH4gZGlzdCwgY2FycykKYGBgCmBgYHtyfQojZ2dwbG90KGNhcnMsIHg9c3BlZWQsIHk9ZGlzdCkKcDEgPC0gZ2dwbG90KCkgKyBnZW9tX2xpbmUoYWVzKHkgPSBkaXN0LCB4ID0gc3BlZWQsIGNvbG9yPXNwZWVkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGNhcnMsIHN0YXQ9ImlkZW50aXR5IikKcDEKYGBgCgpgYGB7cn0gCiNJbnN0YWxsIHBhY2thZ2VzLCBsb2FkIGxpYnJhcmllcywgY2hlY2sgdmVyc2lvbiBvZiBnZ3Bsb3QyCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKbGlicmFyeSgiZ2dwbG90MiIpOyBwYWNrYWdlVmVyc2lvbigiZ2dwbG90MiIpbApgYGAKCmBgYHtyfQojTG9hZCBkYXRhCmRhdGEoIm1pZHdlc3QiLCBwYWNrYWdlID0gImdncGxvdDIiKSAgIyBsb2FkIHRoZSBkYXRhCgojSWYgdGhlIGZpcnN0IGRvZXNuJ3Qgd29yayB0cnkgdGhpcwojIG1pZHdlc3QgPC0gcmVhZC5jc3YoImh0dHA6Ly9nb28uZ2wvRzFLNDFLIikgIyBhbHQgc291cmNlIApgYGAKCmBgYHtyfQojIFR1cm4gb2ZmIHNjaWVudGlmaWMgbm90YXRpb24gbGlrZSAxZSswNgpvcHRpb25zKHNjaXBlbj05OTkpICAKYGBgCgpgYGB7cn0KI2RhdGEgcHJvcGVydGllcwpzdHIobWlkd2VzdCkKYGBgCgpgYGB7cn0KI2RhdGEgZGltZW5zaW9ucwpkaW0obWlkd2VzdCkKYGBgCgpgYGB7cn0KI2RhdGEgcHJvcGVydGllcyAtPiB1bml4LWxpa2UgaGVhZApoZWFkKG1pZHdlc3QpCgpgYGAKCmBgYHtyfQojZGF0YSBwcm9wZXJ0aWVzIC0+IHVuaXgtbGlrZSB0YWlsCnRhaWwobWlkd2VzdCkKYGBgCgpgYGB7cn0KI01ha2UgYSBzaW1wbGUgc2NhdHRlciBwbG90IG9mIHRoZSBkYXRhCmdncGxvdChtaWR3ZXN0LCBhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KI1JlbW92ZSBncmF5IGJhY2tncm91bmQgYW5kIGFzc2lnbiB0byB2YXJpYWJsZXMKcCA8LSBnZ3Bsb3QobWlkd2VzdCwgYWVzKHg9YXJlYSwgeT1wb3B0b3RhbCkpICsgZ2VvbV9wb2ludCgpCnAgPC0gcCArIHRoZW1lX2J3KCkKcHJpbnQocCkKYGBgCgpgYGB7cn0KI1J1biBhIGxpbmVhciBtb2RlbCBvbiB0aGUgZGF0YQojIHNldCBzZT1GQUxTRSB0byB0dXJub2ZmIGNvbmZpZGVuY2UgYmFuZHMKZyA8LSBnZ3Bsb3QobWlkd2VzdCwgYWVzKHg9YXJlYSwgeT1wb3B0b3RhbCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpIApnIDwtIGcgKyB0aGVtZV9idygpCnByaW50KGcpCmBgYAoKYGBge3J9CiMgRGVsZXRlIHRoZSBwb2ludHMgb3V0c2lkZSB0aGUgbGltaXRzCmcgPC0gZyArIHhsaW0oYygwLCAwLjEpKSArIHlsaW0oYygwLCAxMDAwMDAwKSkgICAjIGRlbGV0ZXMgcG9pbnRzCnByaW50KGcpCgpgYGAKCmBgYHtyfQojIFpvb20gaW4gd2l0aG91dCBkZWxldGluZyB0aGUgcG9pbnRzIG91dHNpZGUgdGhlIGxpbWl0cy4gCiMgQXMgYSByZXN1bHQsIHRoZSBsaW5lIG9mIGJlc3QgZml0IGlzIHRoZSBzYW1lIGFzIHRoZSBvcmlnaW5hbCBwbG90LgpnIDwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgIAoKIyBab29tIGluIHdpdGhvdXQgZGVsZXRpbmcgdGhlIHBvaW50cyBvdXRzaWRlIHRoZSBsaW1pdHMuIAojIEFzIGEgcmVzdWx0LCB0aGUgbGluZSBvZiBiZXN0IGZpdCBpcyB0aGUgc2FtZSBhcyB0aGUgb3JpZ2luYWwgcGxvdC4KZzEgPC0gZyArIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwwLjEpLCB5bGltPWMoMCwgMTAwMDAwMCkpICsgdGhlbWVfYncoKSAjIHpvb21zIGluCnByaW50KGcxKQpgYGAKCmBgYHtyfQojIEFkZCBUaXRsZSBhbmQgTGFiZWxzCmcxICsgbGFicyh0aXRsZT0iQXJlYSBWcyBQb3B1bGF0aW9uIiwgc3VidGl0bGU9IkZyb20gbWlkd2VzdCBkYXRhc2V0IiwgeT0iUG9wdWxhdGlvbiIsIHg9IkFyZWEiLCBjYXB0aW9uPSJNaWR3ZXN0IERlbW9ncmFwaGljcyIpCnByaW50KGcxKQpgYGAKCmBgYHtyfQojIEFkZCBUaXRsZSBhbmQgTGFiZWxzCmcxICsgZ2d0aXRsZSgiQXJlYSBWcyBQb3B1bGF0aW9uIiwgc3VidGl0bGU9IkZyb20gbWlkd2VzdCBkYXRhc2V0IikgKyB4bGFiKCJBcmVhIikgKyB5bGFiKCJQb3B1bGF0aW9uIikKYGBgCgpgYGB7cn0KIyBGdWxsIFBsb3QgY2FsbApsaWJyYXJ5KGdncGxvdDIpCnEgPC0gZ2dwbG90KG1pZHdlc3QsIGFlcyh4PWFyZWEsIHk9cG9wdG90YWwpKSArIAogIGdlb21fcG9pbnQoKSArIHRoZW1lX2J3KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwwLjEpLCB5bGltPWMoMCwgMTAwMDAwMCkpICsgCiAgbGFicyh0aXRsZT0iQXJlYSBWcyBQb3B1bGF0aW9uIiwgc3VidGl0bGU9IkZyb20gbWlkd2VzdCBkYXRhc2V0IiwgeT0iUG9wdWxhdGlvbiIsIHg9IkFyZWEiLCBjYXB0aW9uPSJNaWR3ZXN0IERlbW9ncmFwaGljcyIpCnByaW50KHEpCmBgYAoKYGBge3J9CiNDaGFuZ2UgdGhlIGNvbG9ycwpnZ3Bsb3QobWlkd2VzdCwgYWVzKHg9YXJlYSwgeT1wb3B0b3RhbCkpICsgCiAgZ2VvbV9wb2ludChjb2w9InN0ZWVsYmx1ZSIsIHNpemU9MykgKyAgICMgU2V0IHN0YXRpYyBjb2xvciBhbmQgc2l6ZSBmb3IgcG9pbnRzCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbD0iZmlyZWJyaWNrIikgKyAgIyBjaGFuZ2UgdGhlIGNvbG9yIG9mIGxpbmUKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsIDAuMSksIHlsaW09YygwLCAxMDAwMDAwKSkgKyB0aGVtZV9idygpICsKICBsYWJzKHRpdGxlPSJBcmVhIFZzIFBvcHVsYXRpb24iLCBzdWJ0aXRsZT0iRnJvbSBtaWR3ZXN0IGRhdGFzZXQiLCB5PSJQb3B1bGF0aW9uIiwgeD0iQXJlYSIsIGNhcHRpb249Ik1pZHdlc3QgRGVtb2dyYXBoaWNzIikKYGBgCgpgYGB7cn0KI0NvbG9yIGJ5IHN0YXRlCmdnIDwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2w9c3RhdGUpLCBzaXplPTMpICsgICMgU2V0IGNvbG9yIHRvIHZhcnkgYmFzZWQgb24gc3RhdGUgY2F0ZWdvcmllcy4KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgY29sPSJmaXJlYnJpY2siLCBzaXplPTIpICsgdGhlbWVfYncoKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLCAwLjEpLCB5bGltPWMoMCwgMTAwMDAwMCkpICsgCiAgbGFicyh0aXRsZT0iQXJlYSBWcyBQb3B1bGF0aW9uIiwgc3VidGl0bGU9IkZyb20gbWlkd2VzdCBkYXRhc2V0IiwgeT0iUG9wdWxhdGlvbiIsIHg9IkFyZWEiLCBjYXB0aW9uPSJNaWR3ZXN0IERlbW9ncmFwaGljcyIpCnBsb3QoZ2cpCmBgYAoKYGBge3J9CiMgcmVtb3ZlIGxlZ2VuZApnZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iTm9uZSIpICAKYGBgCgpgYGB7cn0KZ2cgKyBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICAjIGNoYW5nZSBjb2xvciBwYWxldHRlCmBgYAoKYGBge3J9CmxpYnJhcnkoUkNvbG9yQnJld2VyKQpoZWFkKGJyZXdlci5wYWwuaW5mbywgMTApIApgYGAKCmBgYHtyfQojIEJhc2UgcGxvdApnZyA8LSBnZ3Bsb3QobWlkd2VzdCwgYWVzKHg9YXJlYSwgeT1wb3B0b3RhbCkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sPXN0YXRlKSwgc2l6ZT0zKSArICAjIFNldCBjb2xvciB0byB2YXJ5IGJhc2VkIG9uIHN0YXRlIGNhdGVnb3JpZXMuCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbD0iZmlyZWJyaWNrIiwgc2l6ZT0yKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwgMC4xKSwgeWxpbT1jKDAsIDEwMDAwMDApKSArIHRoZW1lX2J3KCkgKwogIGxhYnModGl0bGU9IkFyZWEgVnMgUG9wdWxhdGlvbiIsIHN1YnRpdGxlPSJGcm9tIG1pZHdlc3QgZGF0YXNldCIsIHk9IlBvcHVsYXRpb24iLCB4PSJBcmVhIiwgY2FwdGlvbj0iTWlkd2VzdCBEZW1vZ3JhcGhpY3MiKQoKIyBDaGFuZ2UgYnJlYWtzCmdnICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwgMC4xLCAwLjAxKSkKYGBgCgpgYGB7cn0KIyBDaGFuZ2UgYnJlYWtzICsgbGFiZWwgYXMgQWxwaGFiZXQKZ2cgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLCAwLjEsIDAuMDEpLCBsYWJlbHMgPSBsZXR0ZXJzWzE6MTFdKQpgYGAKCmBgYHtyfQojIFJldmVyc2UgWCBBeGlzIFNjYWxlCmdnICsgc2NhbGVfeF9yZXZlcnNlKCkKYGBgCgpgYGB7cn0KIyBDaGFuZ2UgQXhpcyBUZXh0cwpnZyArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsIDAuMSwgMC4wMSksIGxhYmVscyA9IHNwcmludGYoIiUxLjJmJSUiLCBzZXEoMCwgMC4xLCAwLjAxKSkpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwgMTAwMDAwMCwgMjAwMDAwKSwgbGFiZWxzID0gZnVuY3Rpb24oeCl7cGFzdGUwKHgvMTAwMCwgJ0snKX0pCmBgYAoKYGBge3J9CiMgbWV0aG9kIDE6IFVzaW5nIHRoZW1lX3NldCgpIHdpdGggZ3JpZHMKdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkgICMgbm90IHJ1bgpnZwpgYGAKCmBgYHtyfQojIG1ldGhvZCAyOiBBZGRpbmcgdGhlbWUgTGF5ZXIgaXRzZWxmLiB3aXRob3V0IGdyaWRzCmdnICsgdGhlbWVfY2xhc3NpYygpICsgbGFicyhzdWJ0aXRsZT0iQ2xhc3NpYyBUaGVtZSIpCmBgYAoKYGBge3J9CiMgQWRkIHBsb3QgY29tcG9uZW50cyAKZ2cgPC0gZ2dwbG90KG1pZHdlc3QsIGFlcyh4PWFyZWEsIHk9cG9wdG90YWwpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbD1zdGF0ZSwgc2l6ZT1wb3BkZW5zaXR5KSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgc2U9RikgKyB4bGltKGMoMCwgMC4xKSkgKyB5bGltKGMoMCwgNTAwMDAwKSkgKyAKICBsYWJzKHRpdGxlPSJBcmVhIFZzIFBvcHVsYXRpb24iLCB5PSJQb3B1bGF0aW9uIiwgeD0iQXJlYSIsIGNhcHRpb249IlNvdXJjZTogbWlkd2VzdCIpCgojIENhbGwgcGxvdCAKcGxvdChnZykKYGBgCgpgYGB7cn0KIyBCYXNlIFBsb3QKZ2cgPC0gZ2dwbG90KG1pZHdlc3QsIGFlcyh4PWFyZWEsIHk9cG9wdG90YWwpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbD1zdGF0ZSwgc2l6ZT1wb3BkZW5zaXR5KSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgc2U9RikgKyB4bGltKGMoMCwgMC4xKSkgKyB5bGltKGMoMCwgNTAwMDAwKSkgKyAKICBsYWJzKHRpdGxlPSJBcmVhIFZzIFBvcHVsYXRpb24iLCB5PSJQb3B1bGF0aW9uIiwgeD0iQXJlYSIsIGNhcHRpb249IlNvdXJjZTogbWlkd2VzdCIpCgojIE1vZGlmeSB0aGVtZSBjb21wb25lbnRzIApnZyArIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MjAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlPSJib2xkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseT0iQW1lcmljYW4gVHlwZXdyaXRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJ0b21hdG8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0wLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVoZWlnaHQ9MS4yKSwgICMgdGl0bGUKICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseT0iQW1lcmljYW4gVHlwZXdyaXRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2U9ImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0wLjUpLCAgIyBzdWJ0aXRsZQogICAgICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KHNpemU9MTUpLCAgIyBjYXB0aW9uCiAgICAgICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X3RleHQodmp1c3Q9MTAsICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTE1KSwgICMgWCBheGlzIHRpdGxlCiAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X3RleHQoc2l6ZT0xNSksICAjIFkgYXhpcyB0aXRsZQogICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDMwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3Q9LjUpLCAgIyBYIGF4aXMgdGV4dAogICAgICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xMCkpICAjIFkgYXhpcyB0ZXh0CmBgYAoKYGBge3J9CiMjTWV0aG9kIDE6IFVzaW5nIGxhYnMoKQoKIyBCYXNlIFBsb3QKZ2cgPC0gZ2dwbG90KG1pZHdlc3QsIGFlcyh4PWFyZWEsIHk9cG9wdG90YWwpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbD1zdGF0ZSwgc2l6ZT1wb3BkZW5zaXR5KSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgc2U9RikgKyB4bGltKGMoMCwgMC4xKSkgKyB5bGltKGMoMCwgNTAwMDAwKSkgKyAKICBsYWJzKHRpdGxlPSJBcmVhIFZzIFBvcHVsYXRpb24iLCB5PSJQb3B1bGF0aW9uIiwgeD0iQXJlYSIsIGNhcHRpb249IlNvdXJjZTogbWlkd2VzdCIpCgpnZyArIGxhYnMoY29sb3I9IlN0YXRlIiwgc2l6ZT0iRGVuc2l0eSIpICAjIG1vZGlmeSBsZWdlbmQgdGl0bGUKYGBgCgpgYGB7cn0KIyNNZXRob2QgMjogVXNpbmcgZ3VpZGVzKCkKCiMgQmFzZSBQbG90CmdnIDwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2w9c3RhdGUsIHNpemU9cG9wZGVuc2l0eSkpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIHNlPUYpICsgeGxpbShjKDAsIDAuMSkpICsgeWxpbShjKDAsIDUwMDAwMCkpICsgCiAgbGFicyh0aXRsZT0iQXJlYSBWcyBQb3B1bGF0aW9uIiwgeT0iUG9wdWxhdGlvbiIsIHg9IkFyZWEiLCBjYXB0aW9uPSJTb3VyY2U6IG1pZHdlc3QiKQoKZ2cgPC0gZ2cgKyBndWlkZXMoY29sb3I9Z3VpZGVfbGVnZW5kKCJTdGF0ZSIpLCBzaXplPWd1aWRlX2xlZ2VuZCgiRGVuc2l0eSIpKSAgIyBtb2RpZnkgbGVnZW5kIHRpdGxlCnBsb3QoZ2cpCmBgYAoKYGBge3J9CiMjTSBNZXRob2QgMzogVXNpbmcgc2NhbGVfYWVzdGhldGljX3ZhcnR5cGUoKQoKIyBCYXNlIFBsb3QKZ2cgPC0gZ2dwbG90KG1pZHdlc3QsIGFlcyh4PWFyZWEsIHk9cG9wdG90YWwpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbD1zdGF0ZSwgc2l6ZT1wb3BkZW5zaXR5KSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgc2U9RikgKyB4bGltKGMoMCwgMC4xKSkgKyB5bGltKGMoMCwgNTAwMDAwKSkgKyAKICBsYWJzKHRpdGxlPSJBcmVhIFZzIFBvcHVsYXRpb24iLCB5PSJQb3B1bGF0aW9uIiwgeD0iQXJlYSIsIGNhcHRpb249IlNvdXJjZTogbWlkd2VzdCIpCgojIE1vZGlmeSBMZWdlbmQKZ2cgKyBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lPSJTdGF0ZSIpICsgc2NhbGVfc2l6ZV9jb250aW51b3VzKG5hbWUgPSAiRGVuc2l0eSIsIGd1aWRlID0gRkFMU0UpICAjIHR1cm4gb2ZmIGxlZ2VuZCBmb3Igc2l6ZQpgYGAKCmBgYHtyfQojI01hbnVhbCBjb2xvciBzY2FsZQoKIyBCYXNlIFBsb3QKZ2cgPC0gZ2dwbG90KG1pZHdlc3QsIGFlcyh4PWFyZWEsIHk9cG9wdG90YWwpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbD1zdGF0ZSwgc2l6ZT1wb3BkZW5zaXR5KSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgc2U9RikgKyB4bGltKGMoMCwgMC4xKSkgKyB5bGltKGMoMCwgNTAwMDAwKSkgKyAKICBsYWJzKHRpdGxlPSJBcmVhIFZzIFBvcHVsYXRpb24iLCB5PSJQb3B1bGF0aW9uIiwgeD0iQXJlYSIsIGNhcHRpb249IlNvdXJjZTogbWlkd2VzdCIpCgpnZyArIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJTdGF0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJJbGxpbm9pcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJbmRpYW5hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1pY2hpZ2FuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9oaW8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV2lzY29uc2luIiksIAogICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJJTCI9ImJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSU4iPSJyZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUkiPSJncmVlbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPSCI9ImJyb3duIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldJIj0ib3JhbmdlIikpCmBgYAoKYGBge3J9CiNDaGFuZ2UgdGhlIE9yZGVyIG9mIExlZ2VuZAoKIyBCYXNlIFBsb3QKZ2cgPC0gZ2dwbG90KG1pZHdlc3QsIGFlcyh4PWFyZWEsIHk9cG9wdG90YWwpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbD1zdGF0ZSwgc2l6ZT1wb3BkZW5zaXR5KSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgc2U9RikgKyB4bGltKGMoMCwgMC4xKSkgKyB5bGltKGMoMCwgNTAwMDAwKSkgKyAKICBsYWJzKHRpdGxlPSJBcmVhIFZzIFBvcHVsYXRpb24iLCB5PSJQb3B1bGF0aW9uIiwgeD0iQXJlYSIsIGNhcHRpb249IlNvdXJjZTogbWlkd2VzdCIpCgpnZyArIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgICAgc2l6ZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKQpgYGAKCmBgYHtyfQojSG93IHRvIFN0eWxlIHRoZSBMZWdlbmQgVGl0bGUsIFRleHQgYW5kIEtleQoKZ2cgKyB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiwgY29sb3IgPSAiZmlyZWJyaWNrIiksIAogICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgIGxlZ2VuZC5rZXk9ZWxlbWVudF9yZWN0KGZpbGw9J3NwcmluZ2dyZWVuJykpICsgCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9Miwgc3Ryb2tlPTEuNSkpKSAKYGBgCgpgYGB7cn0KIyBObyBsZWdlbmQgCmdnICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJOb25lIikgKyBsYWJzKHN1YnRpdGxlPSJObyBMZWdlbmQiKQoKYGBgCgpgYGB7cn0KIyBsZWdlbmQgYXQgdGhlIGJvdHRvbSBhbmQgaG9yaXpvbnRhbCAKZ2cgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsIGxlZ2VuZC5ib3ggPSAiaG9yaXpvbnRhbCIpICsgbGFicyhzdWJ0aXRsZT0iTGVnZW5kIGF0IEJvdHRvbSIpCmBgYAoKYGBge3J9CiMgbGVnZW5kIGF0IGJvdHRvbS1yaWdodCwgaW5zaWRlIHRoZSBwbG90IApnZyArIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLCBjb2xvciA9ICJzYWxtb24iLCBmYWNlPSJib2xkIiksCiAgICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb249YygxLDApLCAKICAgICAgICAgICBsZWdlbmQucG9zaXRpb249YygwLjk1LCAwLjA1KSwgIAogICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgbGFicyhzdWJ0aXRsZT0iTGVnZW5kOiBCb3R0b20tUmlnaHQgSW5zaWRlIHRoZSBQbG90IikKCmBgYAoKYGBge3J9CiMgbGVnZW5kIGF0IHRvcC1sZWZ0LCBpbnNpZGUgdGhlIHBsb3QgCmdnICsgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGNvbG9yID0gInNhbG1vbiIsIGZhY2U9ImJvbGQiKSwKICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbj1jKDAsMSksIAogICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbj1jKDAuMDUsIDAuOTUpLAogICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgbGFicyhzdWJ0aXRsZT0iTGVnZW5kOiBUb3AtTGVmdCBJbnNpZGUgdGhlIFBsb3QiKQpgYGAKCmBgYHtyfQojIEZpbHRlciByZXF1aXJlZCByb3dzLgptaWR3ZXN0X3N1YiA8LSBtaWR3ZXN0W21pZHdlc3QkcG9wdG90YWwgPiAzMDAwMDAsIF0KbWlkd2VzdF9zdWIkbGFyZ2VfY291bnR5IDwtIGlmZWxzZShtaWR3ZXN0X3N1YiRwb3B0b3RhbCA+IDMwMDAwMCwgbWlkd2VzdF9zdWIkY291bnR5LCAiIikKCiMgQmFzZSBQbG90CmdnIDwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2w9c3RhdGUsIHNpemU9cG9wZGVuc2l0eSkpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIHNlPUYpICsgeGxpbShjKDAsIDAuMSkpICsgeWxpbShjKDAsIDUwMDAwMCkpICsgCiAgbGFicyh0aXRsZT0iQXJlYSBWcyBQb3B1bGF0aW9uIiwgeT0iUG9wdWxhdGlvbiIsIHg9IkFyZWEiLCBjYXB0aW9uPSJTb3VyY2U6IG1pZHdlc3QiKQoKIyBQbG90IHRleHQgYW5kIGxhYmVsIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpnZyArIGdlb21fdGV4dChhZXMobGFiZWw9bGFyZ2VfY291bnR5KSwgc2l6ZT0yLCBkYXRhPW1pZHdlc3Rfc3ViKSArIGxhYnMoc3VidGl0bGU9IldpdGggZ2dwbG90Mjo6Z2VvbV90ZXh0IikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICAgIyB0ZXh0CgpgYGAKCmBgYHtyfQojbGFiZWxzIHdpdGggYm94ZXMKZ2cgKyBnZW9tX2xhYmVsKGFlcyhsYWJlbD1sYXJnZV9jb3VudHkpLCBzaXplPTIsIGRhdGE9bWlkd2VzdF9zdWIsIGFscGhhPTAuMjUpICsgbGFicyhzdWJ0aXRsZT0iV2l0aCBnZ3Bsb3QyOjpnZW9tX2xhYmVsIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICAjIGxhYmVsCmBgYAoKYGBge3J9CiMgUGxvdCB0ZXh0IGFuZCBsYWJlbCB0aGF0IFJFUEVMUyBlYWNob3RoZXIgKHVzaW5nIGdncmVwZWwgcGtnKQojaW5zdGFsbC5wYWNrYWdlcygiZ2dyZXBlbCIpCmxpYnJhcnkoZ2dyZXBlbCkKZ2cgKyBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPWxhcmdlX2NvdW50eSksIHNpemU9MiwgZGF0YT1taWR3ZXN0X3N1YikgKyBsYWJzKHN1YnRpdGxlPSJXaXRoIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbCIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKSAgICMgdGV4dApgYGAKCmBgYHtyfQpnZyArIGdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsPWxhcmdlX2NvdW50eSksIHNpemU9MiwgZGF0YT1taWR3ZXN0X3N1YikgKyBsYWJzKHN1YnRpdGxlPSJXaXRoIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikgICAjIGxhYmVsCmBgYAoKYGBge3J9CiMjSG93IHRvIEFkZCBBbm5vdGF0aW9ucyBBbnl3aGVyZSBpbnNpZGUgUGxvdAoKIyBEZWZpbmUgYW5kIGFkZCBhbm5vdGF0aW9uCmxpYnJhcnkoZ3JpZCkKbXlfdGV4dCA8LSAiVGhpcyB0ZXh0IGlzIGF0IHg9MC43IGFuZCB5PTAuOCEiCm15X2dyb2IgPSBncmlkLnRleHQobXlfdGV4dCwgeD0wLjcsICB5PTAuOCwgZ3A9Z3Bhcihjb2w9ImZpcmVicmljayIsIGZvbnRzaXplPTE0LCBmb250ZmFjZT0iYm9sZCIpKQpnZyArIGFubm90YXRpb25fY3VzdG9tKG15X2dyb2IpCmBgYAoKYGBge3J9CiMgRmxpcCB0aGUgWCBhbmQgWSBheGlzCgojIEJhc2UgUGxvdApnZyA8LSBnZ3Bsb3QobWlkd2VzdCwgYWVzKHg9YXJlYSwgeT1wb3B0b3RhbCkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sPXN0YXRlLCBzaXplPXBvcGRlbnNpdHkpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBzZT1GKSArIHhsaW0oYygwLCAwLjEpKSArIHlsaW0oYygwLCA1MDAwMDApKSArIAogIGxhYnModGl0bGU9IkFyZWEgVnMgUG9wdWxhdGlvbiIsIHk9IlBvcHVsYXRpb24iLCB4PSJBcmVhIiwgY2FwdGlvbj0iU291cmNlOiBtaWR3ZXN0Iiwgc3VidGl0bGU9IlggYW5kIFkgYXhpcyBGbGlwcGVkIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpCgojIEZsaXAgdGhlIFggYW5kIFkgYXhpcyAKZ2cgKyBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KI1JldmVyc2UgdGhlIHNjYWxlIG9mIGFuIGF4aXMKCmdnIDwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeD1hcmVhLCB5PXBvcHRvdGFsKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2w9c3RhdGUsIHNpemU9cG9wZGVuc2l0eSkpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIHNlPUYpICsgeGxpbShjKDAsIDAuMSkpICsgeWxpbShjKDAsIDUwMDAwMCkpICsgCiAgbGFicyh0aXRsZT0iQXJlYSBWcyBQb3B1bGF0aW9uIiwgeT0iUG9wdWxhdGlvbiIsIHg9IkFyZWEiLCBjYXB0aW9uPSJTb3VyY2U6IG1pZHdlc3QiLCBzdWJ0aXRsZT0iQXhpcyBTY2FsZXMgUmV2ZXJzZWQiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikKCiMgUmV2ZXJzZSB0aGUgWCBhbmQgWSBBeGlzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpnZzEgPC0gZ2cgKyBzY2FsZV94X3JldmVyc2UoKSArIHNjYWxlX3lfcmV2ZXJzZSgpCmdnMQpgYGAKCmBgYHtyfQojTXVsdGlwbGUgcGxvdCBhcnJhbmdlbWVudApsaWJyYXJ5KCJncmlkIik7IHBhY2thZ2VWZXJzaW9uKCJncmlkIikKbGlicmFyeSgiZ3JpZEV4dHJhIik7IHBhY2thZ2VWZXJzaW9uKCJncmlkRXh0cmEiKQpgYGAKCmBgYHtyfQojYXJyYW5nZSBwbG90IC0+IHZhciAxCmdyaWQuYXJyYW5nZShnZyxnZzEsIG5jb2w9MSkKYGBgCgpgYGB7cn0KI2FycmFuZ2UgcGxvdCAtPiB2YXIgMgpncmlkLmFycmFuZ2UoZ2csZ2cxLCBuY29sPTIpCmBgYAoKYGBge3J9CiNhcnJhbmdlIHBsb3QgLT4gdmFyIDMKZ3JpZC5hcnJhbmdlKGdnLGdnMSxnZywgbmNvbD0zKQpgYGAKCmBgYHtyfQojcmVhcnJhbmdlIGdncGxvdDIgd2l0aCBvbmUgbGFyZ2UgcGxvdCBvbiB0b3AgYW5kIHR3byBvbiBib3R0b20gCmdyaWQuYXJyYW5nZShnZywgYXJyYW5nZUdyb2IoZ2cxLCBnZywgbmNvbD0yKSwgaGVpZ2h0cz1jKDIvNCwgMS80KSwgbmNvbD0xKSAKCmBgYAoKYGBge3J9CiNyZWFycmFuZ2UgZ2dwbG90MiB3aXRoIG9uZSBsYXJnZSBwbG90IG9uIGJvdHRvbSBhbmQgdHdvIG9uIHRvcApncmlkLmFycmFuZ2UoYXJyYW5nZUdyb2IoZ2cxLCBnZywgbmNvbD0yKSxnZywgaGVpZ2h0cz1jKDEvNCwgMi80KSwgbmNvbD0xKQpgYGAKCmBgYHtyfQojRmFjZXRpbmc6IERyYXcgbXVsdGlwbGUgcGxvdHMgd2l0aGluIG9uZSBmaWd1cmUKZGF0YShtcGcsIHBhY2thZ2U9ImdncGxvdDIiKSAgIyBsb2FkIGRhdGEKIyBtcGcgPC0gcmVhZC5jc3YoImh0dHA6Ly9nb28uZ2wvdUVlUkd1IikgICMgYWx0IGRhdGEgc291cmNlCgpnIDwtIGdncGxvdChtcGcsIGFlcyh4PWRpc3BsLCB5PWh3eSkpICsgCiAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICBsYWJzKHRpdGxlPSJod3kgdnMgZGlzcGwiLCBjYXB0aW9uID0gIlNvdXJjZTogbXBnIikgKwogICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2U9RkFMU0UpICsgCiAgICAgIHRoZW1lX2J3KCkgICMgYXBwbHkgYncgdGhlbWUKcGxvdChnKQpgYGAKCmBgYHtyfQojRmFjZXQgV3JhcAoKIyBCYXNlIFBsb3QKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoeD1kaXNwbCwgeT1od3kpKSArIAogICAgICBnZW9tX3BvaW50KCkgKyAKICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFKSArIAogICAgICB0aGVtZV9idygpICAjIGFwcGx5IGJ3IHRoZW1lCgojIEZhY2V0IHdyYXAgd2l0aCBjb21tb24gc2NhbGVzCmcgKyBmYWNldF93cmFwKCB+IGNsYXNzLCBucm93PTMpICsgbGFicyh0aXRsZT0iaHd5IHZzIGRpc3BsIiwgY2FwdGlvbiA9ICJTb3VyY2U6IG1wZyIsIHN1YnRpdGxlPSJHZ3Bsb3QyIC0gRmFjZXRpbmcgLSBNdWx0aXBsZSBwbG90cyBpbiBvbmUgZmlndXJlIikgICMgU2hhcmVkIHNjYWxlcwpgYGAKCmBgYHtyfQojIEZhY2V0IHdyYXAgd2l0aCBmcmVlIHNjYWxlcwpnICsgZmFjZXRfd3JhcCggfiBjbGFzcywgc2NhbGVzID0gImZyZWUiKSArIGxhYnModGl0bGU9Imh3eSB2cyBkaXNwbCIsIGNhcHRpb24gPSAiU291cmNlOiBtcGciLCBzdWJ0aXRsZT0iR2dwbG90MiAtIEZhY2V0aW5nIC0gTXVsdGlwbGUgcGxvdHMgaW4gb25lIGZpZ3VyZSB3aXRoIGZyZWUgc2NhbGVzIikgICMgU2NhbGVzIGZyZWUKYGBgCgpgYGB7cn0KIyMgRmFjZXQgZ3JpZAojIEJhc2UgUGxvdApnIDwtIGdncGxvdChtcGcsIGFlcyh4PWRpc3BsLCB5PWh3eSkpICsgCiAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICBsYWJzKHRpdGxlPSJod3kgdnMgZGlzcGwiLCBjYXB0aW9uID0gIlNvdXJjZTogbXBnIiwgc3VidGl0bGU9IkdncGxvdDIgLSBGYWNldGluZyAtIE11bHRpcGxlIHBsb3RzIGluIG9uZSBmaWd1cmUiKSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZT1GQUxTRSkgKyAKICAgICAgdGhlbWVfYncoKSAgIyBhcHBseSBidyB0aGVtZQoKIyBBZGQgRmFjZXQgR3JpZApnMSA8LSBnICsgZmFjZXRfZ3JpZChtYW51ZmFjdHVyZXIgfiBjbGFzcykgICMgbWFudWZhY3R1cmVyIGluIHJvd3MgYW5kIGNsYXNzIGluIGNvbHVtbnMKcGxvdChnMSkKYGBgCgpgYGB7cn0KIyBCYXNlIFBsb3QKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoeD1kaXNwbCwgeT1od3kpKSArIAogICAgICBnZW9tX3BvaW50KCkgKyAKICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFKSArIAogICAgICBsYWJzKHRpdGxlPSJod3kgdnMgZGlzcGwiLCBjYXB0aW9uID0gIlNvdXJjZTogbXBnIiwgc3VidGl0bGU9IkdncGxvdDIgLSBGYWNldCBHcmlkIC0gTXVsdGlwbGUgcGxvdHMgaW4gb25lIGZpZ3VyZSIpICsKICAgICAgdGhlbWVfYncoKSAgIyBhcHBseSBidyB0aGVtZQoKIyBBZGQgRmFjZXQgR3JpZApnMiA8LSBnICsgZmFjZXRfZ3JpZChjeWwgfiBjbGFzcykgICMgY3lsIGluIHJvd3MgYW5kIGNsYXNzIGluIGNvbHVtbnMuCnBsb3QoZzIpCmBgYAoKYGBge3J9CiMjTW9kaWZ5aW5nIFBsb3QgQmFja2dyb3VuZCwgTWFqb3IgYW5kIE1pbm9yIEF4aXMKCiMgQmFzZSBQbG90CmcgPC0gZ2dwbG90KG1wZywgYWVzKHg9ZGlzcGwsIHk9aHd5KSkgKyAKICAgICAgZ2VvbV9wb2ludCgpICsgCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZT1GQUxTRSkgKyAKICAgICAgdGhlbWVfYncoKSAgIyBhcHBseSBidyB0aGVtZQoKIyBDaGFuZ2UgUGxvdCBCYWNrZ3JvdW5kIGVsZW1lbnRzIApnICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ2toYWtpJyksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJidXJseXdvb2QiLCBzaXplPTEuNSksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJ0b21hdG8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT0uMjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSwKICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJkYXJrb3JhbmdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTEuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lZW5kID0gImJ1dHQiKSwKICAgICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJkYXJrb3JhbmdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTEuNSkpICsKICAgIGxhYnModGl0bGU9Ik1vZGlmaWVkIEJhY2tncm91bmQiLCAKICAgICAgICAgc3VidGl0bGU9IkhvdyB0byBDaGFuZ2UgTWFqb3IgYW5kIE1pbm9yIGdyaWQsIEF4aXMgTGluZXMsIE5vIEJvcmRlciIpCmBgYAoKYGBge3J9CiMgQ2hhbmdlIFBsb3QgTWFyZ2lucyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpnICsgdGhlbWUocGxvdC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsPSJzYWxtb24iKSwgCiAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygyLCAyLCAxLCAxKSwgImNtIikpICsgICMgdG9wLCByaWdodCwgYm90dG9tLCBsZWZ0CiAgICBsYWJzKHRpdGxlPSJNb2RpZmllZCBCYWNrZ3JvdW5kIiwgc3VidGl0bGU9IkhvdyB0byBDaGFuZ2UgUGxvdCBNYXJnaW4iKSAKYGBgCgpgYGB7cn0KIyNIb3cgdG8gUmVtb3ZlIE1ham9yIGFuZCBNaW5vciBHcmlkLCBDaGFuZ2UgQm9yZGVyLCBBeGlzIFRpdGxlLCBUZXh0IGFuZCBUaWNrcwojIEJhc2UgUGxvdApnIDwtIGdncGxvdChtcGcsIGFlcyh4PWRpc3BsLCB5PWh3eSkpICsgCiAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2U9RkFMU0UpICsgCiAgICAgIHRoZW1lX2J3KCkgICMgYXBwbHkgYncgdGhlbWUKCmcgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZT0iTW9kaWZpZWQgQmFja2dyb3VuZCIsIHN1YnRpdGxlPSJIb3cgdG8gcmVtb3ZlIG1ham9yIGFuZCBtaW5vciBheGlzIGdyaWQsIGJvcmRlciwgYXhpcyB0aXRsZSwgdGV4dCBhbmQgdGlja3MiKSAKYGBgCgpgYGB7cn0KIyBTY2F0dGVycGxvdApnZyA8LSBnZ3Bsb3QobWlkd2VzdCwgYWVzKHg9YXJlYSwgeT1wb3B0b3RhbCkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sPXN0YXRlLCBzaXplPXBvcGRlbnNpdHkpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBzZT1GKSArIAogIHhsaW0oYygwLCAwLjEpKSArIAogIHlsaW0oYygwLCA1MDAwMDApKSArIHRoZW1lX2J3KCkgKwogIGxhYnMoc3VidGl0bGU9IkFyZWEgVnMgUG9wdWxhdGlvbiIsIAogICAgICAgeT0iUG9wdWxhdGlvbiIsIAogICAgICAgeD0iQXJlYSIsIAogICAgICAgdGl0bGU9IlNjYXR0ZXJwbG90IiwgCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogbWlkd2VzdCIpCgpwbG90KGdnKQpgYGAKCmBgYHtyfQojU2NhdHRlcnBsb3QKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoY3R5LCBod3kpKQoKIyBTY2F0dGVycGxvdApnICsgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUYpICsgdGhlbWVfYncoKQogIGxhYnMoc3VidGl0bGU9Im1wZzogY2l0eSB2cyBoaWdod2F5IG1pbGVhZ2UiLCAKICAgICAgIHk9Imh3eSIsIAogICAgICAgeD0iY3R5IiwgCiAgICAgICB0aXRsZT0iU2NhdHRlcnBsb3Qgd2l0aCBvdmVybGFwcGluZyBwb2ludHMiLCAKICAgICAgIGNhcHRpb249IlNvdXJjZTogbWlkd2VzdCIpCmBgYAoKYGBge3J9CiMgU2NhdHRlcnBsb3QKdGhlbWVfc2V0KHRoZW1lX2J3KCkpICAjIHByZS1zZXQgdGhlIGJ3IHRoZW1lLgpnIDwtIGdncGxvdChtcGcsIGFlcyhjdHksIGh3eSkpCmcgKyBnZW9tX2ppdHRlcih3aWR0aCA9IC41LCBzaXplPTEpICsKICBsYWJzKHN1YnRpdGxlPSJtcGc6IGNpdHkgdnMgaGlnaHdheSBtaWxlYWdlIiwgCiAgICAgICB5PSJod3kiLCAKICAgICAgIHg9ImN0eSIsIAogICAgICAgdGl0bGU9IkppdHRlcmVkIFBvaW50cyIpCmBgYAoKYGBge3J9CiMgU2NhdHRlcnBsb3QKdGhlbWVfc2V0KHRoZW1lX2J3KCkpICAjIHByZS1zZXQgdGhlIGJ3IHRoZW1lLgpnIDwtIGdncGxvdChtcGcsIGFlcyhjdHksIGh3eSkpCmcgKyBnZW9tX2NvdW50KGNvbD0idG9tYXRvMyIsIHNob3cubGVnZW5kPUYpICsKICBsYWJzKHN1YnRpdGxlPSJtcGc6IGNpdHkgdnMgaGlnaHdheSBtaWxlYWdlIiwgCiAgICAgICB5PSJod3kiLCAKICAgICAgIHg9ImN0eSIsIAogICAgICAgdGl0bGU9IkNvdW50cyBQbG90IikKYGBgCgpgYGB7cn0KbXBnX3NlbGVjdCA8LSBtcGdbbXBnJG1hbnVmYWN0dXJlciAlaW4lIGMoImF1ZGkiLCAiZm9yZCIsICJob25kYSIsICJoeXVuZGFpIiksIF0KCiMgU2NhdHRlcnBsb3QKdGhlbWVfc2V0KHRoZW1lX2J3KCkpICAjIHByZS1zZXQgdGhlIGJ3IHRoZW1lLgpnIDwtIGdncGxvdChtcGdfc2VsZWN0LCBhZXMoZGlzcGwsIGN0eSkpICsgCiAgbGFicyhzdWJ0aXRsZT0ibXBnOiBEaXNwbGFjZW1lbnQgdnMgQ2l0eSBNaWxlYWdlIiwKICAgICAgIHRpdGxlPSJCdWJibGUgY2hhcnQiKQoKZyArIGdlb21faml0dGVyKGFlcyhjb2w9bWFudWZhY3R1cmVyLCBzaXplPWh3eSkpICsgCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbD1tYW51ZmFjdHVyZXIpLCBtZXRob2Q9ImxtIiwgc2U9RikKYGBgCgpgYGB7cn0KI0RpdmVyZ2luZyBCYXIgcGxvdAoKIyBEYXRhIFByZXAKZGF0YSgibXRjYXJzIikgICMgbG9hZCBkYXRhCm10Y2FycyRgY2FyIG5hbWVgIDwtIHJvd25hbWVzKG10Y2FycykgICMgY3JlYXRlIG5ldyBjb2x1bW4gZm9yIGNhciBuYW1lcwptdGNhcnMkbXBnX3ogPC0gcm91bmQoKG10Y2FycyRtcGcgLSBtZWFuKG10Y2FycyRtcGcpKS9zZChtdGNhcnMkbXBnKSwgMikgICMgY29tcHV0ZSBub3JtYWxpemVkIG1wZwptdGNhcnMkbXBnX3R5cGUgPC0gaWZlbHNlKG10Y2FycyRtcGdfeiA8IDAsICJiZWxvdyIsICJhYm92ZSIpICAjIGFib3ZlIC8gYmVsb3cgYXZnIGZsYWcKbXRjYXJzIDwtIG10Y2Fyc1tvcmRlcihtdGNhcnMkbXBnX3opLCBdICAjIHNvcnQKbXRjYXJzJGBjYXIgbmFtZWAgPC0gZmFjdG9yKG10Y2FycyRgY2FyIG5hbWVgLCBsZXZlbHMgPSBtdGNhcnMkYGNhciBuYW1lYCkgICMgY29udmVydCB0byBmYWN0b3IgdG8gcmV0YWluIHNvcnRlZCBvcmRlciBpbiBwbG90LgoKIyBEaXZlcmdpbmcgQmFyY2hhcnRzCmdncGxvdChtdGNhcnMsIGFlcyh4PWBjYXIgbmFtZWAsIHk9bXBnX3osIGxhYmVsPW1wZ196KSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIGFlcyhmaWxsPW1wZ190eXBlKSwgd2lkdGg9LjUpICArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTWlsZWFnZSIsIAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkFib3ZlIEF2ZXJhZ2UiLCAiQmVsb3cgQXZlcmFnZSIpLCAKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJhYm92ZSI9IiMwMGJhMzgiLCAiYmVsb3ciPSIjZjg3NjZkIikpICsgCiAgbGFicyhzdWJ0aXRsZT0iTm9ybWFsaXNlZCBtaWxlYWdlIGZyb20gJ210Y2FycyciLCAKICAgICAgIHRpdGxlPSAiRGl2ZXJnaW5nIEJhcnMiKSArIAogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQojRGl2ZXJnaW5nIExvbGxpcG9wIENoYXJ0CmdncGxvdChtdGNhcnMsIGFlcyh4PWBjYXIgbmFtZWAsIHk9bXBnX3osIGxhYmVsPW1wZ196KSkgKyAKICBnZW9tX3BvaW50KHN0YXQ9J2lkZW50aXR5JywgZmlsbD0iYmxhY2siLCBzaXplPTYpICArCiAgZ2VvbV9zZWdtZW50KGFlcyh5ID0gMCwgCiAgICAgICAgICAgICAgICAgICB4ID0gYGNhciBuYW1lYCwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gbXBnX3osIAogICAgICAgICAgICAgICAgICAgeGVuZCA9IGBjYXIgbmFtZWApLCAKICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGNvbG9yPSJ3aGl0ZSIsIHNpemU9MikgKwogIGxhYnModGl0bGU9IkRpdmVyZ2luZyBMb2xsaXBvcCBDaGFydCIsIAogICAgICAgc3VidGl0bGU9Ik5vcm1hbGl6ZWQgbWlsZWFnZSBmcm9tICdtdGNhcnMnOiBMb2xsaXBvcCIpICsgCiAgeWxpbSgtMi41LCAyLjUpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KIyBQbG90CmdncGxvdChtdGNhcnMsIGFlcyh4PWBjYXIgbmFtZWAsIHk9bXBnX3osIGxhYmVsPW1wZ196KSkgKyAKICBnZW9tX3BvaW50KHN0YXQ9J2lkZW50aXR5JywgYWVzKGNvbD1tcGdfdHlwZSksIHNpemU9NikgICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iTWlsZWFnZSIsIAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBYm92ZSBBdmVyYWdlIiwgIkJlbG93IEF2ZXJhZ2UiKSwgCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoImFib3ZlIj0iIzAwYmEzOCIsICJiZWxvdyI9IiNmODc2NmQiKSkgKyAKICBnZW9tX3RleHQoY29sb3I9IndoaXRlIiwgc2l6ZT0yKSArCiAgbGFicyh0aXRsZT0iRGl2ZXJnaW5nIERvdCBQbG90IiwgCiAgICAgICBzdWJ0aXRsZT0iTm9ybWFsaXplZCBtaWxlYWdlIGZyb20gJ210Y2Fycyc6IERvdHBsb3QiKSArIAogIHlsaW0oLTIuNSwgMi41KSArCiAgY29vcmRfZmxpcCgpCmBgYAoKYGBge3J9CiMgUHJlcGFyZSBkYXRhOiBncm91cCBtZWFuIGNpdHkgbWlsZWFnZSBieSBtYW51ZmFjdHVyZXIuCmN0eV9tcGcgPC0gYWdncmVnYXRlKG1wZyRjdHksIGJ5PWxpc3QobXBnJG1hbnVmYWN0dXJlciksIEZVTj1tZWFuKSAgIyBhZ2dyZWdhdGUKY29sbmFtZXMoY3R5X21wZykgPC0gYygibWFrZSIsICJtaWxlYWdlIikgICMgY2hhbmdlIGNvbHVtbiBuYW1lcwpjdHlfbXBnIDwtIGN0eV9tcGdbb3JkZXIoY3R5X21wZyRtaWxlYWdlKSwgXSAgIyBzb3J0CmN0eV9tcGckbWFrZSA8LSBmYWN0b3IoY3R5X21wZyRtYWtlLCBsZXZlbHMgPSBjdHlfbXBnJG1ha2UpICAjIHRvIHJldGFpbiB0aGUgb3JkZXIgaW4gcGxvdC4KaGVhZChjdHlfbXBnLCA0KQojPiAgICAgICAgICBtYWtlICBtaWxlYWdlCiM+IDkgICAgIGxpbmNvbG4gMTEuMzMzMzMKIz4gOCAgbGFuZCByb3ZlciAxMS41MDAwMAojPiAzICAgICAgIGRvZGdlIDEzLjEzNTE0CiM+IDEwICAgIG1lcmN1cnkgMTMuMjUwMDAKYGBgCgpgYGB7cn0KIyBEcmF3IHBsb3QKZ2dwbG90KGN0eV9tcGcsIGFlcyh4PW1ha2UsIHk9bWlsZWFnZSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aD0uNSwgZmlsbD0idG9tYXRvMyIpICsgCiAgbGFicyh0aXRsZT0iT3JkZXJlZCBCYXIgQ2hhcnQiLCAKICAgICAgIHN1YnRpdGxlPSJNYWtlIFZzIEF2Zy4gTWlsZWFnZSIsIAogICAgICAgY2FwdGlvbj0ic291cmNlOiBtcGciKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCB2anVzdD0wLjYpKQpgYGAKCmBgYHtyfQojTG9sbGlwb3AgY2hhcnQKIyBQbG90CmdncGxvdChjdHlfbXBnLCBhZXMoeD1tYWtlLCB5PW1pbGVhZ2UpKSArIAogIGdlb21fcG9pbnQoc2l6ZT0zKSArIAogIGdlb21fc2VnbWVudChhZXMoeD1tYWtlLCAKICAgICAgICAgICAgICAgICAgIHhlbmQ9bWFrZSwgCiAgICAgICAgICAgICAgICAgICB5PTAsIAogICAgICAgICAgICAgICAgICAgeWVuZD1taWxlYWdlKSkgKyAKICBsYWJzKHRpdGxlPSJMb2xsaXBvcCBDaGFydCIsIAogICAgICAgc3VidGl0bGU9Ik1ha2UgVnMgQXZnLiBNaWxlYWdlIiwgCiAgICAgICBjYXB0aW9uPSJzb3VyY2U6IG1wZyIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIHZqdXN0PTAuNikpCmBgYAoKYGBge3J9CiMgSGlzdG9ncmFtIG9uIGEgQ29udGludW91cyAoTnVtZXJpYykgVmFyaWFibGUKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoZGlzcGwpKSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKQpnICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGw9Y2xhc3MpLCAKICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gLjEsIAogICAgICAgICAgICAgICAgICAgY29sPSJibGFjayIsIAogICAgICAgICAgICAgICAgICAgc2l6ZT0uMSkgKyAgIyBjaGFuZ2UgYmlud2lkdGgKICBsYWJzKHRpdGxlPSJIaXN0b2dyYW0gd2l0aCBBdXRvIEJpbm5pbmciLCAKICAgICAgIHN1YnRpdGxlPSJFbmdpbmUgRGlzcGxhY2VtZW50IGFjcm9zcyBWZWhpY2xlIENsYXNzZXMiKQpgYGAKCmBgYHtyfQojIEhpc3RvZ3JhbSBvbiBhIENvbnRpbnVvdXMgKE51bWVyaWMpIFZhcmlhYmxlCmcgKyBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1jbGFzcyksIAogICAgICAgICAgICAgICAgICAgYmlucz01LCAKICAgICAgICAgICAgICAgICAgIGNvbD0iYmxhY2siLCAKICAgICAgICAgICAgICAgICAgIHNpemU9LjEpICsgICAjIGNoYW5nZSBudW1iZXIgb2YgYmlucwogIGxhYnModGl0bGU9Ikhpc3RvZ3JhbSB3aXRoIEZpeGVkIEJpbnMiLCAKICAgICAgIHN1YnRpdGxlPSJFbmdpbmUgRGlzcGxhY2VtZW50IGFjcm9zcyBWZWhpY2xlIENsYXNzZXMiKSAKYGBgCgpgYGB7cn0KIyBIaXN0b2dyYW0gb24gYSBDYXRlZ29yaWNhbCB2YXJpYWJsZQpnIDwtIGdncGxvdChtcGcsIGFlcyhtYW51ZmFjdHVyZXIpKQpnICsgZ2VvbV9iYXIoYWVzKGZpbGw9Y2xhc3MpLCB3aWR0aCA9IDAuNSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgdmp1c3Q9MC42KSkgKyAKICBsYWJzKHRpdGxlPSJIaXN0b2dyYW0gb24gQ2F0ZWdvcmljYWwgVmFyaWFibGUiLCAKICAgICAgIHN1YnRpdGxlPSJNYW51ZmFjdHVyZXIgYWNyb3NzIFZlaGljbGUgQ2xhc3NlcyIpIApgYGAKCmBgYHtyfQojIFBsb3QKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoY3R5KSkKZyArIGdlb21fZGVuc2l0eShhZXMoZmlsbD1mYWN0b3IoY3lsKSksIGFscGhhPTAuOCkgKyAKICAgIGxhYnModGl0bGU9IkRlbnNpdHkgcGxvdCIsIAogICAgICAgICBzdWJ0aXRsZT0iQ2l0eSBNaWxlYWdlIEdyb3VwZWQgYnkgTnVtYmVyIG9mIGN5bGluZGVycyIsCiAgICAgICAgIGNhcHRpb249IlNvdXJjZTogbXBnIiwKICAgICAgICAgeD0iQ2l0eSBNaWxlYWdlIiwKICAgICAgICAgZmlsbD0iIyBDeWxpbmRlcnMiKQpgYGAKCmBgYHtyfQojIEJveCBQbG90CmcgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzLCBjdHkpKQpnICsgZ2VvbV9ib3hwbG90KHZhcndpZHRoPVQsIGZpbGw9InBsdW0iKSArIAogICAgbGFicyh0aXRsZT0iQm94IHBsb3QiLCAKICAgICAgICAgc3VidGl0bGU9IkNpdHkgTWlsZWFnZSBncm91cGVkIGJ5IENsYXNzIG9mIHZlaGljbGUiLAogICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IG1wZyIsCiAgICAgICAgIHg9IkNsYXNzIG9mIFZlaGljbGUiLAogICAgICAgICB5PSJDaXR5IE1pbGVhZ2UiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGdndGhlbWVzKQpnIDwtIGdncGxvdChtcGcsIGFlcyhjbGFzcywgY3R5KSkKZyArIGdlb21fYm94cGxvdChhZXMoZmlsbD1mYWN0b3IoY3lsKSkpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIHZqdXN0PTAuNikpICsgCiAgbGFicyh0aXRsZT0iQm94IHBsb3QiLCAKICAgICAgIHN1YnRpdGxlPSJDaXR5IE1pbGVhZ2UgZ3JvdXBlZCBieSBDbGFzcyBvZiB2ZWhpY2xlIiwKICAgICAgIGNhcHRpb249IlNvdXJjZTogbXBnIiwKICAgICAgIHg9IkNsYXNzIG9mIFZlaGljbGUiLAogICAgICAgeT0iQ2l0eSBNaWxlYWdlIikKYGBgCgpgYGB7cn0KIyBkb3QgKyBib3hwbG90CmcgPC0gZ2dwbG90KG1wZywgYWVzKG1hbnVmYWN0dXJlciwgY3R5KSkKZyArIGdlb21fYm94cGxvdCgpICsgCiAgZ2VvbV9kb3RwbG90KGJpbmF4aXM9J3knLCAKICAgICAgICAgICAgICAgc3RhY2tkaXI9J2NlbnRlcicsIAogICAgICAgICAgICAgICBkb3RzaXplID0gLjUsIAogICAgICAgICAgICAgICBmaWxsPSJyZWQiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NjUsIHZqdXN0PTAuNikpICsgCiAgbGFicyh0aXRsZT0iQm94IHBsb3QgKyBEb3QgcGxvdCIsIAogICAgICAgc3VidGl0bGU9IkNpdHkgTWlsZWFnZSB2cyBDbGFzczogRWFjaCBkb3QgcmVwcmVzZW50cyAxIHJvdyBpbiBzb3VyY2UgZGF0YSIsCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IG1wZyIsCiAgICAgICB4PSJDbGFzcyBvZiBWZWhpY2xlIiwKICAgICAgIHk9IkNpdHkgTWlsZWFnZSIpCmBgYAoKYGBge3J9CiMgdmlvbGluIHBsb3QKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoY2xhc3MsIGN0eSkpCmcgKyBnZW9tX3Zpb2xpbigpICsgCiAgbGFicyh0aXRsZT0iVmlvbGluIHBsb3QiLCAKICAgICAgIHN1YnRpdGxlPSJDaXR5IE1pbGVhZ2UgdnMgQ2xhc3Mgb2YgdmVoaWNsZSIsCiAgICAgICBjYXB0aW9uPSJTb3VyY2U6IG1wZyIsCiAgICAgICB4PSJDbGFzcyBvZiBWZWhpY2xlIiwKICAgICAgIHk9IkNpdHkgTWlsZWFnZSIpCmBgYAoKYGBge3J9CiMgcHJlcCBmcmVxdWVuY3kgdGFibGUKZnJlcXRhYmxlIDwtIHRhYmxlKG1wZyRtYW51ZmFjdHVyZXIpCmRmIDwtIGFzLmRhdGEuZnJhbWUudGFibGUoZnJlcXRhYmxlKQpoZWFkKGRmKQogICAgIApgYGAKCmBgYHtyfQojIEJhclBsb3QKZyA8LSBnZ3Bsb3QoZGYsIGFlcyhWYXIxLCBGcmVxKSkKZyArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGggPSAwLjUsIGZpbGw9InRvbWF0bzIiKSArIAogICAgICBsYWJzKHRpdGxlPSJCYXIgQ2hhcnQiLCAKICAgICAgICAgICBzdWJ0aXRsZT0iTWFudWZhY3R1cmVyIG9mIHZlaGljbGVzIiwgCiAgICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBGcmVxdWVuY3kgb2YgTWFudWZhY3R1cmVycyBmcm9tICdtcGcnIGRhdGFzZXQiKSArCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCB2anVzdD0wLjYpKQpgYGAKCgoK